package com.openbravo.data.loader.dialect;

import com.openbravo.data.basic.BasicException;
import com.openbravo.data.loader.TableDBSentenceBuilder;
import com.openbravo.data.loader.serialize.serializer.write.SerializerWriteBasic;
import com.openbravo.data.loader.sqlbuilder.NormalBuilder;
import e.odbo.data.model.*;
import e.odbo.data.util.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

public abstract class AbstractDDLFactory {
    protected static Logger loger= LoggerFactory.getLogger(AbstractDDLFactory.class);

    protected AbstractDBDialect dbDialect;

    protected Map<String,Object> props=new HashMap<>();

    public static Map<String, Object> defaultConfigs = new HashMap<>();

    public AbstractDDLFactory(AbstractDBDialect dbDialect) {
        this.dbDialect = dbDialect;
    }

    public  void init(Map<String,Object> props){
        this.props=props;
    }

    public Map<String,ColumnType> dialectTypeMap=new HashMap<>();

    protected Object getConfig(String entryName) {
        Object ret = this.props.get(entryName);
        if (ret == null)
            ret = defaultConfigs.get(entryName);
        return ret;
    }


    //DataBase
    public  String createAndUseDataBase(String dataBaseName){
        return createDataBase(dataBaseName)+" USE "+dataBaseName+getDialectTailed();
    }


    public abstract String createDataBase(String dataBaseName);


    public String dropDataBase(String dataBaseName){
        return "DROP DATABASE "+dataBaseName+getDialectTailed();
    }

    //Table
    public  String createTableDropFirst(Table table){
        return "DROP TABLE IF EXISTS "+table.getName()+getDialectTailed()+createTable(table);
    }

    public String getDialectTailed(){
        return ";\r\n";
    }


    public String dropTable(Table table) {

        StringBuilder builder=new StringBuilder("DROP TABLE "+table.getName()+getDialectTailed());

        return builder.toString();
    }

    public String dropTable(String table) {
        StringBuilder builder=new StringBuilder("DROP TABLE "+table+getDialectTailed());
        //String[] indexs=table.getMuxIndexs()

        return builder.toString();
    }


    public String createTable(Table table) {
        StringBuilder ddl=new StringBuilder("CREATE TABLE "+table.getName()+" (");
        for(Column column:table.getColumns())
            ddl.append(generateColumn(column)+",");

        if(table.isPK())
            ddl.append(generatePK(table.getPKColumns())+",");

        Map<String, String[]> indexs = table.getIndexs();
        String[] indexNames= indexs.keySet().toArray(new String[]{});
        for(int i=0;i<indexNames.length;i++)
            ddl.append(generateIndex(indexNames[i],indexs.get(indexNames[i]))+",");

        for(TableForeignKey foreignKey:table.getForeignKeys()){
            ddl.append(generateForeign(foreignKey)+",");
        }

        String ret=ddl.substring(0,ddl.length()-1);
        ret+=")"+generateTableDialect(table)+getDialectTailed();

        return ret;
    }

    protected abstract String generateTableDialect(Table table);

    protected abstract String generateForeign(TableForeignKey foreignKey);

    protected abstract String generateIndex(String indexName, String[] strings);

    protected abstract String generatePK(List<Column> pkColumns);

    protected abstract String generateColumn(Column column);

    protected ColumnType transformColumnTypeToDialect(Column column,String dialect){
        ColumnType base=column.getColumnType();
        if(!base.getDialectName().equalsIgnoreCase(ColumnType.DIALECT_Default))
            return base;
        ColumnType dist=column.getDialectColumnType(dialect);
        if(dist==null)
            dist=dialectTypeMap.get(base.getType());
        if(dist!=null){
            return base.union(dist);
        }
        return base;
    }

    public String addColumn(String table, Column column) {
        return "ALTER TABLE "+table+" ADD "+generateColumn(column)+getDialectTailed() ;
    }

    public String deleteColumn(String table, String column) {
        return "ALTER TABLE "+table+" DROP "+column+getDialectTailed();
    }

    public String changeColumn(String table, Column column) {
        return "ALTER TABLE "+table+" CHANGE "+generateColumn(column)+getDialectTailed() ;
    }

    public String changeColumn(String table,String oldColumn, Column column) {
        return "ALTER TABLE "+table+" CHANGE "+oldColumn+" "+generateColumn(column)+getDialectTailed() ;
    }


    public String createIndex(String table, String... columns) {
        String name=table+'_'+ StringUtils.join(columns,'_');
        return "CREATE INDEX "+name+" ON "+table+" ("+StringUtils.join(columns,',')+")"+getDialectTailed();
    }


    public String createIndex(String table, String column) {
        return "CREATE INDEX "+table+"_"+column+" ON "+table+" ("+column+")"+getDialectTailed();
    }


    public String dropIndex(String table, String column) {
        return "DROP INDEX "+table+"."+table+"_"+column+getDialectTailed();
    }

    public String dropIndex(String table, String... columns) {
        return "DROP INDEX "+table+"."+StringUtils.join(columns,'_')+getDialectTailed();
    }

    public String createForeignKey(TableForeignKey foriegnKey) {
        return "ALTER TABLE "+foriegnKey.getTable()+ " ADD " +generateForeign(foriegnKey)+getDialectTailed();
    }


    public String dropForignKey(String table, String[] columns) {
        return "ALTER TABLE "+table+ " DROP FOREIGN KEY "+buildNameKey(table,columns)+getDialectTailed() ;
    }

    public String dropForignKey(String table,String forignKeyName) {
        return "ALTER TABLE "+table+ " DROP FOREIGN KEY "+forignKeyName+getDialectTailed() ;
    }


    //util
    protected static String buildNameKey(String table,String ...columns){
        StringBuilder base=new StringBuilder(table);
        for(String column:columns)
            base.append("_"+column);
        return base.toString();
    }


    //全局生成函数
    public String  createTablesDropFirst(Table ...tables){
        StringBuilder ret =new StringBuilder();
        for(Table table:tables)
            ret.append(createTableDropFirst(table));
        return ret.toString();
    }

    public AbstractDBDialect getDbDialect(){
        return this.dbDialect;
    }

    public String insert(TableData data) {
        StringBuilder builder=new StringBuilder();
        NormalBuilder sqlBuilder=new NormalBuilder(TableDBSentenceBuilder.BaseQuerySqlBuilder.getInsert(data.getTable(),data.getAllColumn()));
        for(Row row:data.getDatas()) {
            try {
                builder.append(sqlBuilder.getSQL(new SerializerWriteBasic(data.getAllTypes()), data.getAllValue(row.getValue())) + getDialectTailed());
            } catch (BasicException e) {
                loger.error(row.toString());
                e.printStackTrace();
            }
        }
        return builder.toString();
    }

    public String remove(TableData data) {
        StringBuilder builder=new StringBuilder();
        NormalBuilder sqlBuilder=new NormalBuilder(TableDBSentenceBuilder.BaseQuerySqlBuilder.getDelete(data.getTable(),data.getColumn()));
        for(Row row:data.getDatas())
            try {
            builder.append(sqlBuilder.getSQL(new SerializerWriteBasic(data.getTypes()),row.getValue())+getDialectTailed());
            } catch (BasicException e) {
                loger.error(row.toString());
                e.printStackTrace();
            }
        return builder.toString();
    }


    public String getScript(DataBase database){
        StringBuilder sqlBuilder=new StringBuilder();

        if(database.isSubModel()){
            //remove column;
            for(String table:database.getRemoveColumn().keySet()){
                for(String column:database.getRemoveColumn().get(table))
                    sqlBuilder.append(deleteColumn(table,column));
            }

            //alite column
            for(String table:database.getAlterColumn().keySet()){
                for(Column column:database.getAlterColumn().get(table))
                    sqlBuilder.append(changeColumn(table,column));
            }

            //addData column
            for(String table:database.getAddColumn().keySet())
                for(Column column:database.getAddColumn().get(table))
                    sqlBuilder.append(addColumn(table,column));
        }

        //addData table
        for(Table table:database.getTables())
            sqlBuilder.append(createTable(table));

        //addData index
        for(Index index:database.getAddIndex())
            sqlBuilder.append(createIndex(index.getTable(),index.getColumns()));

        //addData foreignKey
        for(TableForeignKey foreignKey:database.getAddForeignKeys())
            sqlBuilder.append(createForeignKey(foreignKey));

        //addData datas
        for(TableData data:database.getAddData()) {
            sqlBuilder.append(insert(data));
        }
        return sqlBuilder.toString();
    }

    public String getMigrationUpScript(Migration...migrations){
        StringBuilder builder=new StringBuilder();
        for(Migration migration:migrations)
            getMigrationUpScript(migration,builder);
        return builder.toString();
    }

    protected void getMigrationUpScript(Migration migration,StringBuilder sqlBuilder){
        //remove index
        for(Index index:migration.getRemoveIndex())
            sqlBuilder.append(dropIndex(index.getTable(),index.getColumns()));

        //remove foreignKey
        for(String table:migration.getRemoveForeignKeys().keySet()) {
            for(String foreignKey:migration.getRemoveForeignKeys().get(table))
                sqlBuilder.append(dropForignKey(table,foreignKey));
        }
        //remove datas;
        for(TableData data:migration.getRemoveData())
            sqlBuilder.append(remove(data));

        //remove column;
        for(String table:migration.getRemoveColumn().keySet()){
            for(String column:migration.getRemoveColumn().get(table))
                sqlBuilder.append(deleteColumn(table,column));
        }

        //remove table;
        for(String table:migration.getRemoveTable())
            sqlBuilder.append(dropTable(table));

        //alite column
        for(String table:migration.getAlterColumn().keySet()){
            for(Column column:migration.getAlterColumn().get(table))
                sqlBuilder.append(changeColumn(table,column));
        }

        //addData column
        for(String table:migration.getAddColumn().keySet())
            for(Column column:migration.getAddColumn().get(table))
                sqlBuilder.append(addColumn(table,column));

        //addData table
        for(Table table:migration.getAddTable())
            sqlBuilder.append(createTable(table));

        //addData index
        for(Index index:migration.getAddIndex())
            sqlBuilder.append(createIndex(index.getTable(),index.getColumns()));
        //addData foreignKey
        for(TableForeignKey foreignKey:migration.getAddForeignKeys())
            sqlBuilder.append(createForeignKey(foreignKey));
        //addData datas
        for(TableData data:migration.getAddData()) {
            sqlBuilder.append(insert(data));
        }
    }

    public String getMigrationDownScript(Migration ...migrations){
        StringBuilder builder=new StringBuilder();
        for(Migration migration:migrations)
            getMigrationDownScript(migration,builder);
        return builder.toString();
    }

     /*
         @Todu
      */
    protected void getMigrationDownScript(Migration migration,StringBuilder sqlBuilder){

    }
}
